#include "tissue_types.h"
#include "random.h"

#include "ParseText.h"
#include "UCI_TumorGrowth/GaussianFilter.h"

Tissue_Type* pFirst_Tissue_Type = NULL;
Tissue_Type* pLast_Tissue_Type = NULL;

using namespace std;
using std::basic_string;

bool Tissues_Initiated = false;

Tissue_Type::Tissue_Type()
{
 Tissue_Name = "unassigned";
 ECM_density = 0;
 Blood_vessel_density = 0;
 structure_value = 1;
 Color.Red = 255;
 Color.Green = 255;
 Color.Blue = 255;
 
 ECM_noise = 0.0;
 
 pPrevious_Tissue_Type = NULL;
 pNext_Tissue_Type = NULL;
 
 /* 
   Uncomment the following code if you would prefer 
   automatic management of the linked list structure. 
   However, note that any use of the class will result 
   in the addition of a new member to the list if you 
   do!
 */
 
/* 
 if( pLast_Tissue_Type )
 {
  pLast_Tissue_Type->pNext_Tissue_Type = this;
  pPrevious_Tissue_Type = pLast_Tissue_Type;
 }
 else
 {
  pFirst_Tissue_Type = this;
  pPrevious_Tissue_Type = NULL;
 }
  
 pLast_Tissue_Type = this;
 pNext_Tissue_Type = NULL;
*/
 
 return;
}

Tissue_Type::~Tissue_Type()
{
 Tissue_Name.clear();
 
 // Join the surrounding members in the linked list structure
 
 if( pPrevious_Tissue_Type )
 {
  pPrevious_Tissue_Type->pNext_Tissue_Type = pNext_Tissue_Type;
 }
 if( pNext_Tissue_Type )
 { 
  pNext_Tissue_Type->pPrevious_Tissue_Type = pPrevious_Tissue_Type; 
 }
 return;
}

void Tissue_Type::display( void )
{
 cout << Tissue_Name.c_str() << ":: " << endl
      << "color:       ( " << (int) Color.Red << " , "
	  << (int) Color.Green << " , " 
	  << (int) Color.Blue << " )" << endl
	  << "ECM:         " << ECM_density << endl
	  << "ECM noise:   " << ECM_noise << endl
	  << "Structure:   " << structure_value << endl
	  << "vasculature: " << Blood_vessel_density << endl << endl;
 return;
}

bool InitializeTissueTypesFromXML( const char* XML_filename )
{
 // If tissue types have already been defined, 
 // delete them. 
 
 if( pFirst_Tissue_Type )
 {
  Tissue_Type* pCurrent = pLast_Tissue_Type;
  while( pCurrent && pCurrent->pPrevious_Tissue_Type )
  {
   pCurrent = pCurrent->pPrevious_Tissue_Type;
   delete ( pCurrent->pNext_Tissue_Type );
  }
  delete pFirst_Tissue_Type;
 }

 pFirst_Tissue_Type = NULL;
 pLast_Tissue_Type = NULL;
 
 // load the XML document 
 
 TiXmlDocument doc( XML_filename );
 if( !doc.LoadFile() )
 {
  cout << "Error: XML tissue properties file " << XML_filename << " not found!" << endl;
  return false; 
 }

 TiXmlHandle hDoc(&doc);
 TiXmlElement* pElem;
 TiXmlHandle hRoot(0);

 pElem=hDoc.FirstChildElement().Element();
 // should always have a valid root but handle gracefully if it does
 if( !pElem )
 { 
  cout << "Error: XML tissue properties file " << XML_filename << " is messed up!" << endl;
  return false;
 }

 TiXmlElement* pCurrentElement = NULL;
 
 // Look for all defined tissues and add them to the linked list structure 

 pCurrentElement = FindChildElement( pElem , "tissue" );
  
 while( pCurrentElement )
 {
  Tissue_Type* temp_tissue;
  temp_tissue = new Tissue_Type;
  temp_tissue->Tissue_Name = GetStringChildElement( pCurrentElement, "name");
  
  temp_tissue->ECM_density = GetDoubleChildElement( pCurrentElement, "ECM" );
  temp_tissue->structure_value = GetDoubleChildElement( pCurrentElement, "Structure" );
  temp_tissue->Blood_vessel_density = GetDoubleChildElement( pCurrentElement, "Blood_Vessels" );
  
  temp_tissue->ECM_noise = GetDoubleChildElement( pCurrentElement, "ECM_noise" );

  char* ColorText = GetStringChildElement( pCurrentElement, "color");
  temp_tissue->Color = OrderedTripleToRGBApixel( ColorText );
  
  if( !pLast_Tissue_Type )
  {
   pFirst_Tissue_Type = temp_tissue;
   pLast_Tissue_Type = temp_tissue;
  }
  else
  {
   pLast_Tissue_Type->pNext_Tissue_Type = temp_tissue;
   temp_tissue->pPrevious_Tissue_Type = pLast_Tissue_Type;
   pLast_Tissue_Type = temp_tissue;
  }

  pCurrentElement = FindNextChildElement( pElem , pCurrentElement, "tissue" );
 }
 
 Tissues_Initiated = true;
 
 return true;
}

RGBApixel OrderedTripleToRGBApixel( char* Triple )
{
 RGBApixel Output;
 Output.Red = 255;
 Output.Green = 255;
 Output.Blue = 255;
 int i=0;
 int k=0;
 
 // find the red position
 
 while( !isdigit( Triple[i] ) )
 { i++; }
 int RedStart = i;
 while( isdigit( Triple[i] ) )
 { i++; }
 int RedEnd = i-1;
 
 // extract red
 
 char Temp [3];
 for( int j=0; j < 3 ; j++ )
 { Temp[j] = '\0'; } 
 k=0;
 for( int j=RedStart ; j <= RedEnd ; j++ )
 { Temp[k] = Triple[j]; k++; }
 Output.Red = (ebmpBYTE) atoi( Temp );
 
 // find the green position
 
 while( !isdigit( Triple[i] ) )
 { i++; }
 int GreenStart = i;
 while( isdigit( Triple[i] ) )
 { i++; }
 int GreenEnd = i-1;
 
 // extract green
 
 for( int j=0; j < 3 ; j++ )
 { Temp[j] = '\0'; } 
 k=0;
 for( int j=GreenStart ; j <= GreenEnd ; j++ )
 { Temp[k] = Triple[j]; k++; }
 Output.Green= (ebmpBYTE) atoi( Temp );

 // find the blue position
 
 while( !isdigit( Triple[i] ) )
 { i++; }
 int BlueStart = i;
 while( isdigit( Triple[i] ) )
 { i++; }
 int BlueEnd = i-1;

 // extract blue
 
 for( int j=0; j < 3 ; j++ )
 { Temp[j] = '\0'; } 
 k=0;
 for( int j=BlueStart ; j <= BlueEnd ; j++ )
 { Temp[k] = Triple[j]; k++; }
 Output.Blue = (ebmpBYTE) atoi( Temp );
 return Output; 
}

void DisplayColor( RGBApixel Color )
{
 cout << " ( " << (int) Color.Red  << " , " 
      <<          (int) Color.Green << " , " 
      <<     	  (int) Color.Blue  << " ) " << endl;
 return;
}

bool DisplayAllTissueTypes( void )
{
 Tissue_Type* pCurrent_Tissue_Type = pFirst_Tissue_Type;
 while( pCurrent_Tissue_Type )
 {
  pCurrent_Tissue_Type->display();
  pCurrent_Tissue_Type = pCurrent_Tissue_Type->pNext_Tissue_Type;
 }
 return true;
}

Tissue_Type* FindBestTissueMatch( RGBApixel InputColor )
{
 Tissue_Type* pCurrent = pFirst_Tissue_Type;
 
 Tissue_Type* pBest = NULL;
 int BestMatch = 999999999;
 
 while( pCurrent )
 {
  int TempNorm = 0;
  int temp = (int) InputColor.Red - (int) (pCurrent->Color).Red;
  TempNorm += temp*temp; 
  temp = (int) InputColor.Green - (int) (pCurrent->Color).Green;
  TempNorm += temp*temp; 
  temp = (int) InputColor.Blue - (int) (pCurrent->Color).Blue;
  TempNorm += temp*temp; 
  
  if( TempNorm < BestMatch )
  { 
   BestMatch = TempNorm;
   pBest = pCurrent;
  }
 
  pCurrent = pCurrent->pNext_Tissue_Type; 
 }

 return pBest;
}

bool ImportTissues( const char* ImageFile )
{
 extern Matrix ECM;
 extern Matrix Structure;
 extern Matrix BloodVesselDensity;
 
 extern double _EPS;
 
 if( !Tissues_Initiated )
 { return false; }
 
 BMP TissueImage;
 TissueImage.ReadFromFile( ImageFile );
 
 ECM = zeros( TissueImage.TellWidth() , TissueImage.TellHeight() );
 Structure = zeros( TissueImage.TellWidth() , TissueImage.TellHeight() );
 BloodVesselDensity = zeros( TissueImage.TellWidth() , TissueImage.TellHeight() );
 
 for( int j=0; j < ECM.TellCols() ; j++ )
 {
  for( int i=0; i < ECM.TellRows() ; i++ )
  {
   int I = i;
   int J = TissueImage.TellHeight()-1-j;
  
   Tissue_Type* pBestMatch = FindBestTissueMatch( *TissueImage(i,j) );
   *ECM(I,J) = pBestMatch->ECM_density;
   
   if( pBestMatch->ECM_noise > 1e-9 )
   { 
    *ECM(I,J) += (pBestMatch->ECM_noise)*NormalRandom(); 
	if( *ECM(I,J) <= _EPS )
	{ *ECM(I,J) = 0.0; }
   }
   
   *Structure(I,J) = pBestMatch->structure_value;
   *BloodVesselDensity(I,J) = pBestMatch->Blood_vessel_density;
  }
 }

 extern char* ParameterFile;
 bool SmoothImportedECM = (bool) RetrieveDoubleParameter( ParameterFile , "SmoothImportedECM" );
 
 if( SmoothImportedECM )
 {
  extern Matrix X;
  extern Matrix Y;
  
  double sd = RetrieveDoubleParameter( ParameterFile , "ImportedECM_SmoothingStandardDeviation" );
  ECM = GaussianFilterWithReflection(X,Y,ECM,sd);
 }
 

 return true;
}

